import errMagFun from "./errMsg";
const mapDataFun = (_this) => (dataKey = [], _instance) => {
    if (!dataKey instanceof Array) return errMagFun('mapDataNoArr', dataKey)
    if (dataKey.length === 0) return errMagFun('mapDataEmpty');
    if (!_instance.rStoreData) _instance.rStoreData = {}
    dataKey.forEach(item => {
        if (typeof item !== 'string') throw 'The argument to mapData must be an array string'
        Object.defineProperty(_instance.rStoreData, item, {
            get() {
                return _this.data[item]
            },
            set(value) {
                return _this.data[item] = value
            }
        })
    })
}

const responseCallbackFun = (_this) => (fun) => {
    if (typeof fun !== 'function') throw 'Reactive callbacks must be functions'
    _this.reactive_callback_function_.push(fun)
    return () => {
        const index = _this.reactive_callback_function_.indexOf(fun)
        if (index !== -1) {
            _this.reactive_callback_function_.splice(index, 1)
        }
    }
}

const Response = (_this, key) => {
    _this.reactive_callback_function_.forEach(item => item(_this.data))
    const responseArr = _this.the_react_instance_array_.filter(firstItem => {
        if (key instanceof Array) {
            return key.some(secendItme => {

                return firstItem.keys.indexOf(secendItme) !== -1
            })
        } else {
            return firstItem.keys.indexOf(key) !== -1
        }
    })
    responseArr.forEach(item => {
        item._react.forceUpdate()
    })
    return true
}

const creatNewData = (_this, newData) => {
    const data = { ...newData }
    let fluterKeys = []
    _this.reactive_callback_function_ = []
    _this.the_react_instance_array_ = []
    const handler = {
        get(obj, key) {
            if (typeof obj[key] === 'object' && obj[key] !== null) {
                if (fluterKeys.length === 0) fluterKeys.push(key)
                return new Proxy(obj[key], handler);
            }
            return Reflect.get(obj, key)
        },
        set(obj, prop, value) {
            if (obj instanceof Array) {
                if (prop === 'length') {
                    Response(_this, fluterKeys)
                }
                return Reflect.set(obj, prop, value)
            }
            if (typeof value === 'object') {
                const keys = Object.keys(value)
                if (keys.length > 0) {
                    if (keys.indexOf('_rstore_special_character_') !== -1) {
                        fluterKeys.push(prop)
                        Reflect.set(obj, prop, value.value)
                        if (keys.indexOf('_rstore_immediately_character_') !== -1) {
                            Response(_this, fluterKeys)
                            fluterKeys = []
                        }
                        return true
                    }
                }
            }
            if (obj[prop] === value) return true
            Reflect.set(obj, prop, value)
            return Response(_this, prop)

        },
        deleteProperty(obj, prop) {
            if (prop.indexOf('_rstore_special_character_') !== -1) {
                const propsCount = prop.replace('_rstore_special_character_', '')
                fluterKeys.push(propsCount)
                return Reflect.deleteProperty(obj, propsCount)
            }
            fluterKeys.push(prop)
            Reflect.deleteProperty(obj, prop)
            return Response(_this, fluterKeys)
        },
    }
    return new Proxy(data, handler)
}

const bindDataFun = (_this) => (dataKay = [], _instance = false) => {
    if (typeof _instance !== 'object') throw 'The second parameter to bindData must be the component instance'
    if (!(dataKay instanceof Array)) throw 'The first argument to bindData must be an array string or a string'
    if (dataKay.length === 0) return
    if (_this.the_react_instance_array_.every(firstItem => {
        if (firstItem) {
            if (firstItem._react === _instance) {
                dataKay.forEach(secendItem => {
                    if (firstItem.keys.indexOf(secendItem) === -1) {
                        firstItem.keys.push(secendItem)
                    }
                })
                return false
            }
        }
        return true
    })) {
        const keys = dataKay instanceof Array ? [...dataKay] : [dataKay]
        _this.the_react_instance_array_.push({ _react: _instance, keys })
    }
}
const clearDataFun = (_this) => (dataKeys = []) => {
    if (!(dataKeys instanceof Array)) return errMagFun('clearDataKeysNoArr', dataKeys)
    if (dataKeys.length === 0) return errMagFun('clearDataKeysEmpty');
    const dataKay = Object.keys(_this.data)
    let key = dataKeys.filter(item => dataKay.indexOf(item) !== -1)
    key.forEach((item, index) => {
        if (typeof item !== 'string') throw 'The argument to  clearData must be an array string'
        if (index === key.length - 1) return delete (_this.data[item])
        delete (_this.data[`${item}_rstore_special_character_`])
    })
}


const unBindDataFun = (_this) => (keyArr, _instance) => {
    if (!keyArr || !(keyArr instanceof Array)) throw 'The second argument to unDynamic must be an array of strings'
    _this.the_react_instance_array_ = _this.the_react_instance_array_.filter((item) => {
        if (item._react === _instance) {
            item.keys = item.keys.filter(item => {
                return keyArr.indexOf(item) === -1
            })
            if (item.keys.length === 0) {
                return false
            }
        }
        return true
    })
}

const changeDataFun = (_this) => (obj) => {
    if (typeof obj !== 'object') errMagFun('noObj', obj)
    const dataKay = Object.keys(_this.data)
    let key = Object.keys(obj).filter(item => dataKay.indexOf(item) !== -1)
    key.forEach((item, index) => {
        if (typeof item !== 'string') throw 'The argument to  changeData must be an array string'
        const result = { _rstore_special_character_: true, value: obj[item] }
        if (index === key.length - 1) result._rstore_immediately_character_ = true
        _this.data[item] = result
    })
}

export default (_this, Data = {}, options) => {
    if (!options) return
    _this.data = creatNewData(_this, Data)
    _this.clearData = clearDataFun(_this)
    _this.changeData = changeDataFun(_this)
    _this.bindData = bindDataFun(_this)
    _this.unBindData = unBindDataFun(_this)
    _this.mapData = mapDataFun(_this)
    _this.responseCallback = responseCallbackFun(_this)

}